package com.yonyou.sto.common.domain;

import cn.hutool.core.date.DatePattern;
import cn.hutool.core.date.DateUtil;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.yonyou.sto.modules.ims.model.ImsInvoiceItem;
import com.yonyou.sto.modules.ims.service.impl.OpenBillServiceImpl;
import com.yonyou.sto.modules.ums.service.ConfigNameServic;
import com.yonyou.sto.modules.ums.util.ConfigNameUtil;
import io.jsonwebtoken.Jwts;
import io.jsonwebtoken.SignatureAlgorithm;
import io.jsonwebtoken.impl.compression.CompressionCodecs;
import org.apache.http.HttpEntity;
import org.apache.http.HttpResponse;
import org.apache.http.NameValuePair;
import org.apache.http.client.HttpClient;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.conn.ssl.SSLConnectionSocketFactory;
import org.apache.http.conn.ssl.TrustStrategy;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.ssl.SSLContextBuilder;
import org.apache.http.util.EntityUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import javax.net.ssl.HostnameVerifier;
import javax.net.ssl.SSLContext;
import javax.net.ssl.SSLSession;
import java.io.IOException;
import java.io.InputStream;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.security.*;
import java.security.cert.CertificateException;
import java.util.*;
import java.util.Map.Entry;

/**
 * @date 2018/5/25
 * 该样例代码为调用开蓝票接口代码，适用于JDK1.6及更高版本，jdk1.6版本需要对签名方法稍做修改，修改方法在签名方法内已经写明
 * 请求参数的注意事项也在参数构建的过程中写明，请详细阅读样例代码。
 */
@Component
public class OpenBillBuilder {

    @Value("${yonyoucloud.key_path}")
    private String KEY_PATH;

    @Value("${yonyoucloud.password}")
    private String PASSWORD;

    @Autowired
    private ConfigNameServic configNameServic;
    private static CloseableHttpClient createSSLClientDefault() {
        try {
            SSLContext sslContext = new SSLContextBuilder().loadTrustMaterial(null, new TrustStrategy() {
                @Override
                public boolean isTrusted(java.security.cert.X509Certificate[] chain, String authType) throws CertificateException {
                    return true;
                }
            }).build();

            SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(sslContext, new HostnameVerifier() {
                @Override
                public boolean verify(String s, SSLSession sslSession) {
                    return true;
                }
            });
            return HttpClients.custom().setSSLSocketFactory(sslsf).build();
        } catch (KeyManagementException | KeyStoreException | NoSuchAlgorithmException e) {
            e.printStackTrace();
        }
        return HttpClients.createDefault();
    }


    public Map<String, String> callInvoiceApply(String url, Map<String, String> paramsMap) {

        Map<String, String> resultMap = new HashMap<>();
        try {
//        提供两种构建HttpClient实例的方法，如果使用被注释掉的方法构建实例报证书不被信任的错误，那么请使用未被注释的构建方法
//        HttpClient httpClient = HttpClients.custom().build();
            HttpClient httpClient = createSSLClientDefault();    //信任所有https证书
            HttpPost httpPost = new HttpPost(url);
            // 签名
            String sign = this.sign(paramsMap);
            httpPost.addHeader("sign", sign);
            // 转换POST表单参数
            List<NameValuePair> list = new ArrayList<NameValuePair>();
            Iterator<Entry<String, String>> iterator = paramsMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Entry<String, String> elem = iterator.next();
                list.add(new BasicNameValuePair(elem.getKey(), elem.getValue()));
            }
            if (list.size() > 0) {
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
                httpPost.setEntity(entity);
            }
            // 发送http post请求，并得到响应结果
            HttpResponse response = httpClient.execute(httpPost);
            if (response != null) {
                HttpEntity resEntity = response.getEntity();
                if (resEntity != null) {
                    String result = EntityUtils.toString(resEntity, "UTF-8");
                    Gson gson = new Gson();
                    resultMap = gson.fromJson(result, paramsMap.getClass());
                    return resultMap;
                }
            }
            resultMap.put("code", "9999");
            resultMap.put("msg", "返回数据为空");
            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public Map<String, String> callRedInvoiceApply(String url, Map<String, String> paramsMap) {

        Map<String, String> resultMap = new HashMap<>();
        try {
//        提供两种构建HttpClient实例的方法，如果使用被注释掉的方法构建实例报证书不被信任的错误，那么请使用未被注释的构建方法
//        HttpClient httpClient = HttpClients.custom().build();
            HttpClient httpClient = createSSLClientDefault();    //信任所有https证书
            HttpPost httpPost = new HttpPost(url);
            // 签名
            String sign = this.sign(paramsMap);
            httpPost.addHeader("sign", sign);
            httpPost.addHeader("Content-Type", "application/json");
            // 转换POST表单参数
            List<NameValuePair> list = new ArrayList<NameValuePair>();
            Iterator<Entry<String, String>> iterator = paramsMap.entrySet().iterator();
            while (iterator.hasNext()) {
                Entry<String, String> elem = iterator.next();
                list.add(new BasicNameValuePair(elem.getKey(), elem.getValue()));
            }
            if (list.size() > 0) {
                UrlEncodedFormEntity entity = new UrlEncodedFormEntity(list, "UTF-8");
                httpPost.setEntity(entity);
            }
            // 发送http post请求，并得到响应结果
            HttpResponse response = httpClient.execute(httpPost);
            if (response != null) {
                HttpEntity resEntity = response.getEntity();
                if (resEntity != null) {
                    String result = EntityUtils.toString(resEntity, "UTF-8");
                    Gson gson = new Gson();
                    resultMap = gson.fromJson(result, paramsMap.getClass());
                    return resultMap;
                }
            }
            resultMap.put("code", "9999");
            resultMap.put("msg", "返回数据为空");
            return resultMap;
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    /**
     * 签名
     *
     * @param paramsMap 表单参数
     * @return 签名值
     * @throws Exception
     */
    private String sign(Map<String, String> paramsMap) throws Exception {

        PrivateKey privateKey = loadPrivateKeyOfCA();
        Map<String, Object> claims =
                JwtParamBuilder.build().setSubject("tester").setIssuer("einvoice").setAudience("einvoice")
                        .addJwtId().addIssuedAt().setExpirySeconds(3600).setNotBeforeSeconds(3600).getClaims();
        // 需要将表单参数requestdatas的数据进行md5加密，然后放到签名数据的requestdatas中。
        // 此签名数据必须存在，否则在验证签名时会不通过。
        if (paramsMap.containsKey("requestdatas")) {
            String value = paramsMap.get("requestdatas");
            claims.put("requestdatas", getMD5(value));
        }
        else if (paramsMap.containsKey("nsrsbh")) {
            String value = paramsMap.get("nsrsbh");
            claims.put("nsrsbh", getMD5(value));
        } else {
            //throw new Exception("签名错误");
        }
        // 使用jdk1.6版本时，删除下面代码的中.compressWith(CompressionCodecs.DEFLATE)
        String compactJws = Jwts.builder().signWith(SignatureAlgorithm.RS512, privateKey)
                .setClaims(claims).compressWith(CompressionCodecs.DEFLATE).compact();
        return compactJws;
    }

//    /**
//     * 当在linux环境下运行代码，签名方法报空指针异常的时候，采用该签名方法可以避免这个问题
//     * 使用该方法需要添加新的maven依赖，如下：
//     * <dependency>
//     *     <groupId>com.auth0</groupId>
//     *     <artifactId>java-jwt</artifactId>
//     *     <version>3.3.0</version>
//     * </dependency>
//     * @param paramsMap
//     * @return
//     * @throws Exception
//     */
//    private String sign(Map<String, String> paramsMap) throws Exception {
//
//        RSAPrivateKey privateKey = loadPrivateKeyOfCA();
//        Map<String, Object> claims =
//                JwtParamBuilder.build().setSubject("tester").setIssuer("einvoice").setAudience("einvoice")
//                        .addJwtId().addIssuedAt().setExpirySeconds(300).setNotBeforeSeconds(300).getClaims();
//        String token;
//        try {
//            Algorithm algorithm = Algorithm.RSA512(null, privateKey);
//            token = JWT.create()
//                    .withClaim("requestdatas",getMD5(paramsMap.get("requestdatas")))
//                    .withHeader(claims)
//                    .sign(algorithm);
//        } catch (JWTCreationException exception){
//            //Invalid Signing configuration / Couldn't convert Claims.
//            return null;
//        }
//
//        return token;
//    }

    /**
     * 计算参数MD5
     *
     * @param str
     * @return
     * @throws UnsupportedEncodingException
     * @throws NoSuchAlgorithmException
     */
    private static String getMD5(String str) throws UnsupportedEncodingException,
            NoSuchAlgorithmException {
        byte[] buf = null;
        buf = str.getBytes("utf-8");
        MessageDigest md5 = null;
        md5 = MessageDigest.getInstance("MD5");
        md5.update(buf);
        byte[] tmp = md5.digest();
        StringBuilder sb = new StringBuilder();
        for (byte b : tmp) {
            sb.append(String.format("%02x", b & 0xff));
        }
        return sb.toString();
    }


    /**
     * 读取证书私钥
     *
     * @return
     * @throws UnrecoverableKeyException
     * @throws KeyStoreException
     * @throws NoSuchAlgorithmException
     * @throws CertificateException
     * @throws IOException
     */
    protected PrivateKey loadPrivateKeyOfCA() throws UnrecoverableKeyException, KeyStoreException,
            NoSuchAlgorithmException, CertificateException, IOException {

        System.out.println(KEY_PATH);
        InputStream inputStream = this.getClass().getClassLoader().getResourceAsStream(KEY_PATH);
        KeyStore ks = KeyStore.getInstance("pkcs12");
        ks.load(inputStream, PASSWORD.toCharArray());
        String alias = ks.aliases().nextElement();

        PrivateKey caprk = (PrivateKey) ks.getKey(alias, PASSWORD.toCharArray());
        return caprk;
    }


    /**
     * post表单数据
     *
     * @return
     */
    private Map<String, String> buildPostParam() {
        Map<String, String> paramsMap = new HashMap<String, String>();
        paramsMap.put("requestdatas", this.buildRequestDatas());
        paramsMap.put("email", this.buildEmailConfigs());
        paramsMap.put("sms", this.buildSmsConfigs());
        paramsMap.put("url", this.buildUrlConfigs());
        paramsMap.put("autoAudit", "false");
        return paramsMap;
    }

    /**
     * url回掉配置
     *
     * @return
     */
    private String buildUrlConfigs() {
        List<Object> datas = new ArrayList<>();
        Map<String, Object> data = new HashMap<>();
        data.put("fpqqlsh", createSerialNumber());
        data.put("url", "");
        datas.add(data);
        GsonBuilder builder = new GsonBuilder();
        return builder.create().toJson(datas);
    }

    /**
     * 构造短信发送信息
     *
     * @return
     */
    private String buildSmsConfigs() {
        List<Object> datas = new ArrayList<>();
        Map<String, Object> data = new HashMap<>();
        data.put("fpqqlsh", createSerialNumber());
        data.put("address", "");
        datas.add(data);
        GsonBuilder builder = new GsonBuilder();
        return builder.create().toJson(datas);
    }

    /**
     * 构造email发送信息
     *
     * @return
     */
    private String buildEmailConfigs() {
        List<Object> datas = new ArrayList<>();
        Map<String, Object> data = new HashMap<>();
        data.put("fpqqlsh", createSerialNumber());
        data.put("address", "");
        datas.add(data);
        GsonBuilder builder = new GsonBuilder();
        return builder.create().toJson(datas);
    }

    /**
     * 构造requestdatas
     *
     * @return
     */
    private String buildRequestDatas() {
        List<Object> datas = new ArrayList<>();
        Map<String, Object> data = new HashMap<>();
        data.put("FPQQLSH", createSerialNumber());
        //测试环境请一定要使用测试纳税人识别号
        data.put("XSF_NSRSBH", "201609140000001");
        data.put("GMF_MC", "购买方名称");
        data.put("GMF_DZDH", "购买方地址电话");
        //组织编码，测试环境请一定使用测试环境的组织编码
        data.put("ORGCODE", "20160914001");
        data.put("JSHJ", 1395.00);
        data.put("items", buildItems(null,1));
        datas.add(data);
        GsonBuilder builder = new GsonBuilder();
        return builder.create().toJson(datas);
    }

    /**
     * 构造request发票明细
     *
     * @return
     */
    public List<Object> buildItems(List<ImsInvoiceItem> invoiceItems, Integer contain) {
        List<Object> items = new ArrayList<>();
        int hh=1;
        for (ImsInvoiceItem i: invoiceItems) {
            //configNameServic.getCustCofigStringName(i);
            Map<String, Object> data = new HashMap<>();


            data.put("XMMC", i.getCustmatname());//商品名称
            data.put("GGXH", i.getProductSpec());
            data.put("DW", i.getProductUnit());
            data.put("XMSL", i.getProductQuantity());

            //data.put("XMJSHJ", i.getApplyje());
            if(i.getTaxAmount().doubleValue()==0){
                data.put("XMHSDJ", i.getTaxPrice());
            }else {
                if(contain==0){
                    data.put("XMJE",i.getWsje());//无税金额
                }else if (contain==1){
                    data.put("XMJSHJ", i.getTaxAmount());//价税合计
                }else if (contain==2){
                    data.put("XMHSDJ", i.getTaxPrice());//含税单价
                }else if (contain==3){
                    data.put("XMDJ", i.getUntaxPrice());//无税单价
                }
            }



            data.put("HH", hh);
            hh++;
            // 税率16%需要写成0.16的格式
            data.put("SL", i.getTaxRate().divide(new BigDecimal(100)).setScale(2));
            // SPBM字段为商品税收分类编码，不同的商品会有不同的编码，不对应的话会影响报税，需要咨询下公司财务
            // data.put("SPBM", "3040502029902000000");
            data.put("SPBM", i.getProductSn());//*
            items.add(data);
        }


        return items;
    }


    /**
     * 获取发票请求流水号
     * 长度不超过20位，长度在1到20位的字母和数字组合，不可以重复的，不要包含window系统文件名限制的特殊字符
     * 产生一个随机字符串 年月日时分秒毫秒+3位大小写随机组合的字母
     *
     * @return 发票请求流水号
     */
    public static String createSerialNumber() {
        StringBuilder sb = new StringBuilder(DateUtil.format(new Date(), DatePattern.PURE_DATETIME_MS_PATTERN));
        StringBuilder randomCode = new StringBuilder();
        String model = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
        char[] m = model.toCharArray();
        for (int j = 0; j < 3; j++) {
            char c = m[(int) (Math.random() * 52)];
            randomCode.append(c);
        }
        sb.append(randomCode);
        return sb.toString();
    }
}
